package com.hawk.eye.server.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.hawk.eye.common.core.ApiResult;
import com.hawk.eye.common.core.RCode;
import com.hawk.eye.common.utils.*;
import com.hawk.eye.common.utils.http.OkHttpUtil;
import com.hawk.eye.common.page.PageQuery;
import com.hawk.eye.common.page.PageResult;
import com.hawk.eye.framework.utils.*;
import com.hawk.eye.server.constant.AuthHeaderConstant;
import com.hawk.eye.server.constant.ReqTypeConstant;
import com.hawk.eye.server.convert.RepairApiConvert;
import com.hawk.eye.server.domain.dto.req.RepairApiLastDataReqDTO;
import com.hawk.eye.server.domain.dto.req.RepairApiLogReqDTO;
import com.hawk.eye.server.domain.dto.req.RepairApiReqDTO;
import com.hawk.eye.server.domain.dto.resp.*;
import com.hawk.eye.server.domain.po.ProblemRecord;
import com.hawk.eye.server.domain.po.RepairApi;
import com.hawk.eye.server.domain.po.RepairToProblem;
import com.hawk.eye.server.domain.vo.req.RepairToProblemReqVO;
import com.hawk.eye.server.mapper.RepairApiMapper;
import com.hawk.eye.server.mapper.RepairToLogMapper;
import com.hawk.eye.server.service.*;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 【请填写功能名称】Service业务层处理
 *
 * @author salad
 * @date 2023-10-23
 */
@Service
@RequiredArgsConstructor
public class RepairApiServiceImpl extends ServiceImpl<RepairApiMapper, RepairApi> implements IRepairApiService {

    private final RepairApiMapper repairApiMapper;

    private final RepairToLogMapper repairToLogMapper;

    private final IRepairApiLogService repairApiLogService;

    private final IServiceProblemService problemService;

    @Override
    public PageResult<RepairApiRespDTO> page(PageQuery pageQuery, RepairApiReqDTO reqDTO) {
        IPage<RepairApiRespDTO> pages = repairApiMapper.page(pageQuery.toPage(), reqDTO);
        return PageResult.of(pages);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(RepairApiReqDTO reqDTO) {
        List<RepairToProblemReqVO> problemList = reqDTO.getProblemList();
        RepairApi ra = RepairApiConvert.INSTANCE.to(reqDTO);
        boolean success = saveOrUpdate(ra);
        for (RepairToProblemReqVO reqVO : problemList) {
            ExceptionUtils.isEmptyThrow(reqVO.getId(), "参数[problemId]不允许为空");
            RepairToProblem rtl = new RepairToProblem();
            rtl.setBaseAlarmId(reqVO.getBaseAlarmId());
            rtl.setProblemId(reqVO.getId());
            rtl.setRepairApiId(ra.getId());
            repairToLogMapper.insert(rtl);
        }
        return success;
    }

    @Override
    public RepairApiRespDTO detail(Long id) {
        ExceptionUtils.isEmptyThrow(id, "参数[id]不能为空");
        RepairApiRespDTO respDTO = baseMapper.detail(id);
        List<ProblemRecord> problemList = problemService.getProblemListByRepairId(respDTO.getId());
        respDTO.setProblemList(RepairApiConvert.INSTANCE.to3(problemList));
        return respDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(RepairApiReqDTO reqDTO) {
        ExceptionUtils.isEmptyThrow(reqDTO.getId(), "参数[id]不能为空");
        List<RepairToProblemReqVO> problemList = reqDTO.getProblemList();
        RepairApi ra = RepairApiConvert.INSTANCE.to(reqDTO);
        boolean success = saveOrUpdate(ra);
        repairToLogMapper.deletetByRepairId(reqDTO.getId());
        for (RepairToProblemReqVO reqVO : problemList) {
            ExceptionUtils.isEmptyThrow(reqVO.getId(), "参数[problemId]不允许为空");
            RepairToProblem rtl = new RepairToProblem();
            rtl.setBaseAlarmId(reqVO.getBaseAlarmId());
            rtl.setProblemId(reqVO.getId());
            rtl.setRepairApiId(ra.getId());
            repairToLogMapper.insert(rtl);
        }
        return success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean executeRepairApi(RepairApiReqDTO reqDTO) {
        ExceptionUtils.isEmptyThrow(reqDTO.getId(), "系统异常,参数不能为空,请稍后重试");
        RepairApi repairApi = getById(reqDTO.getId());
        ExceptionUtils.isEmptyThrow(repairApi, "该修复脚本已被其它用户删除,请刷新界面");
        ExceptionUtils.isEmptyThrow(repairApi.getReqType(), "请维护请求方式，GET/POST");
        ExceptionUtils.isEmptyThrow(repairApi.getReqUri(), "请维护请求地址");
        Boolean success = Boolean.TRUE;
        long cost = 0;
        LocalDateTime execTime = LocalDateTime.now();
        ApiResult<?> result = ApiResult.fail();
        String failMessage = null;
        Map<String, String> params = parseReqParams(reqDTO, repairApi.getReqUri());
        Map<String, String> body = parseReqBody(reqDTO, repairApi.getReqBody());
        Map<String, String> header = parseReqHeaders(reqDTO, repairApi.getReqHeaders());
        long start = System.currentTimeMillis();
        String resultFromClient;
        String reqUri = repairApi.getReqUri();
        if (reqUri.contains(StringPool.QUESTION_MARK)) {
            reqUri = StringUtil.sub(reqUri, 0, reqUri.indexOf("?"));
        }
        try {
            if (ReqTypeConstant.GET.equal(repairApi.getReqType())) {
                resultFromClient = OkHttpUtil.get(reqUri, header, params);
            } else {
                resultFromClient = OkHttpUtil.postJson(repairApi.getReqUri(), header, JsonUtil.toJson(body));
            }
            cost = System.currentTimeMillis() - start;
            result = JsonUtil.parse(resultFromClient, new TypeReference<ApiResult<?>>() {
            });
        } catch (Exception e) {
            success = Boolean.FALSE;
            failMessage = ExceptionUtil.getExceptionMessage(e);
        }
        Long userId = SecurityUtils.getUserId();
        RepairApiLogReqDTO build = RepairApiLogReqDTO
                .builder()
                .execSuccessful(RCode.SUCCESS.getCode() == result.getCode() ? 1 : 0)
                .cost(cost).execTime(execTime).execUser(userId)
                .result(failMessage != null ? failMessage : JsonUtil.toJson(result))
                .repairApiId(repairApi.getId())
                .reqType(repairApi.getReqType())
                .reqUri(reqUri).reqParams(JsonUtil.toJson(params))
                .reqBody(JsonUtil.toJson(body)).reqHeaders(JsonUtil.toJson(header))
                .build();
        repairApiLogService.saveExecLog(build);
        RepairApiLastDataReqDTO lastDataReqDTO = RepairApiLastDataReqDTO
                .builder()
                .id(repairApi.getId()).lastExecSuccessful(RCode.SUCCESS.getCode() == result.getCode() ? 1 : 0)
                .lastExecTime(execTime).lastExecUser(userId)
                .build();
        this.updateLastExecData(lastDataReqDTO);
        return success;
    }

    @NotNull
    private Map<String, String> parseReqHeaders(RepairApiReqDTO reqDTO, String reqHeaders) {
        Map<String, String> header = new HashMap<>(16);
        String headersAsString = reqDTO.getHeadersAsString();
        if (StringUtil.isNotBlank(headersAsString)) {
            List<Map<String, String>> reqHeadersNames = JsonUtil.parse(reqHeaders, new TypeReference<List<Map<String, String>>>() {
            });
            Set<String> headerNames = reqHeadersNames.stream().map(e -> e.get("name")).collect(Collectors.toSet());
            List<Map<String, String>> reqHeadersList = JsonUtil.parse(headersAsString, new TypeReference<List<Map<String, String>>>() {
            });
            for (Map<String, String> map : reqHeadersList) {
                if (StringUtil.isBlank(map.get("headerName")) || StringUtil.isBlank(map.get("headerValue"))) {
                    continue;
                }
                if (map.get("isAuthorization") != null && AuthHeaderConstant.IS_AUTHORIZATION.equals(map.get("isAuthorization")) && map.get("type") != null) {
                    String type = map.get("type");
                    if (!AuthHeaderConstant.AUTH_HEADER_OPTIONS.contains(type)) {
                        continue;
                    }
                    map.put("headerValue", type + " " + map.get("headerValue"));
                    header.put(map.get("headerName"), map.get("headerValue"));
                } else {
                    if (headerNames.contains(map.get("headerName"))) {
                        header.put(map.get("headerName"), map.get("headerValue"));
                    }
                }
            }
        }
        return header;
    }

    @NotNull
    private Map<String, String> parseReqParams(RepairApiReqDTO reqDTO, String reqUri) {
        String reqParams = reqDTO.getParamsAsString();
        Map<String, String> params = new HashMap<>(16);
        if (StringUtils.isNotEmpty(reqParams)) {
            Set<String> reqParamNames = new HashSet<>();
            int index = StringUtil.indexOf(reqUri, '?');
            if (index != -1) {
                String paramsAsString = StringUtil.sub(reqUri, index + 1, reqUri.length());
                Map<String, String> query = StringUtil.parseUriParams(paramsAsString);
                reqParamNames = query.keySet();
            }
            List<Map<String, String>> mapList = JsonUtil.parse(reqParams, new TypeReference<List<Map<String, String>>>() {
            });
            for (Map<String, String> map : mapList) {
                ExceptionUtils.isFalseThrow(reqParamNames.contains(map.get("paramName")), "非法的参数[{}]", map.get("paramName"));
                params.put(map.get("paramName"), map.get("paramValue"));
            }
        }
        return params;
    }

    @NotNull
    private Map<String, String> parseReqBody(RepairApiReqDTO reqDTO, String reqBody) {
        String bodyAsString = reqDTO.getBodyAsString();
        Map<String, String> body = new HashMap<>(16);
        if (StringUtil.isNotBlank(bodyAsString) && StringUtil.isNotBlank(reqBody)) {
            Map<String, String> mapList = JsonUtil.parse(bodyAsString, new TypeReference<Map<String, String>>() {
            });
            Map<String, String> reqBodyMap = JsonUtil.parse(reqBody, new TypeReference<Map<String, String>>() {
            });
            Set<String> bodys = reqBodyMap.keySet();
            for (String key : mapList.keySet()) {
                if (bodys.contains(key)) {
                    body.put(key, mapList.get(key));
                }
            }
        }
        return body;
    }

    @Override
    public void updateLastExecData(RepairApiLastDataReqDTO reqDTO) {
        RepairApi ra = getById(reqDTO.getId());
        if (reqDTO.getLastExecSuccessful() == 1) {
            Long successCount = (ra.getExecSuccessCount() == null ? 0 : ra.getExecSuccessCount()) + 1;
            ra.setExecSuccessCount(successCount);
        }
        Long execCount = (ra.getExecCount() == null ? 0 : ra.getExecCount()) + 1;
        ra.setExecCount(execCount);
        ra.setLastExecUser(reqDTO.getLastExecUser());
        ra.setLastExecTime(reqDTO.getLastExecTime());
        ra.setLastExecSuccessful(reqDTO.getLastExecSuccessful());
        updateById(ra);
    }

    @Override
    public PageResult<RepairApiLogRespDTO> repairApiLogList(Long repairId, PageQuery pageQuery) {
        return repairApiLogService.page(pageQuery.toPage(), repairId);
    }

    @Override
    public RepairApiRespDTO getRepairApiByStackHash(String stackHash) {
        return repairApiMapper.getRepairApiByStackHash(stackHash);
    }

    @Override
    public RepairApiParamRespDTO getExecuteRepairParams(Long id) {
        RepairApiParamRespDTO result = new RepairApiParamRespDTO();
        RepairApi repairApi = getById(id);
        if (StringUtil.isNotBlank(repairApi.getReqHeaders())) {
            List<Map<String, String>> headers = JsonUtil.parse(repairApi.getReqHeaders(), new TypeReference<List<Map<String, String>>>() {
            });
            if (CollectionUtil.isNotEmpty(headers)) {
                List<Map<String, String>> parseHeaders = new ArrayList<>();
                for (Map<String, String> map : headers) {
                    if (map.get("name") != null) {
                        String value = map.get("value");
                        if (StringUtil.isBlank(value)) {
                            value = null;
                        }
                        Map<String, String> param = new HashMap<>(16);
                        param.put("headerName", map.get("name"));
                        param.put("headerValue", value);
                        parseHeaders.add(param);
                    }
                }
                result.setHeaders(parseHeaders);
            }
        }
        String reqUri = repairApi.getReqUri();
        if (ReqTypeConstant.GET.equal(repairApi.getReqType()) && StringUtil.isNotBlank(reqUri)) {
            int index = StringUtil.indexOf(reqUri, '?');
            if (index != -1) {
                String paramsAsString = StringUtil.sub(reqUri, index + 1, reqUri.length());
                Map<String, String> query = StringUtil.parseUriParams(paramsAsString);
                result.setParams(parsePlaceholderParams(query));
            }
        }
        String reqBody = repairApi.getReqBody();
        if (ReqTypeConstant.POST.equal(repairApi.getReqType()) && StringUtil.isNotBlank(reqBody)) {
            result.setBody(reqBody);
        }
        return result;
    }

    private List<Map<String, String>> parsePlaceholderParams(Map<String, String> query) {
        List<Map<String, String>> params = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(query)) {
            for (String key : query.keySet()) {
                String value = query.get(key);
                if (StringUtil.isBlank(value)) {
                    value = null;
                }
                Map<String, String> param = new HashMap<>(16);
                param.put("paramName", key);
                param.put("paramValue", value);
                params.add(param);
            }
        }
        return params;
    }


}
